package xq.gwt.mvc.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

public class ArrayListPropertyModel extends AbstractPropertyModel implements PropertyChangedListener {
		
	private static final long serialVersionUID = 3276730896332926737L;
	private ArrayList<PropertyModel> value = new ObservableArrayList();
	private String separator = ";";
	private boolean blockPropertyChanged;
	private Class defaultChildType = StringPropertyModel.class;
			
	public ArrayList<PropertyModel> getValue(){
		return value;
	}
	
	public void setValue(PropertyModel[] value){
		blockPropertyChanged = false;
		Object oldValue = this.value.clone();
		blockPropertyChanged = true;		
		this.value.clear();
		this.value.addAll(Arrays.asList(value));
		blockPropertyChanged = false;
		notifyPropertyChanged(oldValue, value);		
	}
	
	protected void notifyPropertyChnanged(Object oldValue, Object newValue){
		if(!blockPropertyChanged)
			super.notifyPropertyChanged(oldValue, newValue);
	}
	
	public String getSeparator() {
		return separator;
	}

	public void setSeparator(String separator) {
		this.separator = separator;
	}

	@Override
	public String getText() {
		if (value.size() == 0)
			return null;
		String result = "";
		boolean hasValue = false;
		boolean allEmpty = true;
		for (PropertyModel prop : value) {
			String propText = prop.getText();
			if( propText!= null){				 
				result += propText + separator;
				hasValue = true;
				if(!propText.equals(""))
					allEmpty= false;
			}
		}
		if(!hasValue)
			return null;
		if(allEmpty)
			return "";
		result = result.substring(0,result.lastIndexOf(separator));
		return result;
	}

	@Override
	public void setText(String text) throws ConversionException {
		Object oldValue = getValue().clone();
		if((text == null)||(text.equals(""))){
			clearPropertyChangedListeners();
			for (PropertyModel prop : getValue()) {
				prop.setText(text);
			}		
			return;
		}
						
		String[] values = text.split(separator); 
		getValue().clear();
		for (int i = 0; i < values.length; i++) {
			try {
				if(i>=getValue().size()){
					addNewItem();
				}
				getValue().get(i).setText(values[i]);
			} catch (IndexOutOfBoundsException e) {}			
		}
		notifyPropertyChanged(oldValue, getValue());
	}
	
	public boolean isValid(){
		
		for (PropertyModel prop : getValue()) {
			if(!prop.isValid())
				return false;
		}
		
		return super.isValid();		
	}
	
	public Object getObjectValue() {		
		return getValue();
	}

	public Class getPropertyType() {		
		return getValue().getClass();
	}

	public Class getDefaultChildType() {
		return defaultChildType;
	}

	public void setDefaultChildType(Class defaultChildType) {			
		this.defaultChildType = defaultChildType;
	}
	
	public class ObservableArrayList extends ArrayList<PropertyModel>{
		
		public boolean add(PropertyModel o){
			Object oldValue = super.clone(); 
			boolean result = super.add(o);			
			notifyPropertyChanged(oldValue, this);
			propertyModelAdded(o);
			return result; 
		}
		
		public void add(int index,PropertyModel element){
			Object oldValue = super.clone(); 
			super.add(index, element);			
			notifyPropertyChanged(oldValue, this);
			propertyModelAdded(element);
		}
		
		public boolean addAll(Collection<? extends PropertyModel> c){
			Object oldValue = super.clone(); 
			boolean result = super.addAll(c);			
			notifyPropertyChanged(oldValue, this);
			for (PropertyModel prop : c) {
				propertyModelAdded(prop);
			}
			return result;
		}
		
		public boolean addAll(int index, Collection<? extends PropertyModel> c){
			Object oldValue = super.clone(); 
			boolean result = super.addAll(index,c);			
			notifyPropertyChanged(oldValue, this);
			for (PropertyModel prop : c) {
				propertyModelAdded(prop);
			}			
			return result;
		}
		
		public void clear(){
			Object oldValue = super.clone();
			clearPropertyChangedListeners();
			super.clear();
			notifyPropertyChanged(oldValue, this);
		}
		
		public PropertyModel remove(int index){
			Object oldValue = super.clone();
			PropertyModel result = super.remove(index);			
			propertyModelRemoved(result);
			notifyPropertyChanged(oldValue, this);
			return result;			
		}
		
		public boolean remove(Object o){
			Object oldValue = super.clone(); 
			boolean result = super.remove(o);			
			if(result){
				PropertyModel prop = (PropertyModel)o;
				propertyModelRemoved(prop);
			}
			notifyPropertyChanged(oldValue, this);			
			return result;
		}
		
		public boolean removeAll(Collection<?> c){
			Object oldValue = super.clone();
			boolean result = super.removeAll(c);			
			for (Iterator iter = c.iterator(); iter.hasNext();) {
				PropertyModel prop = (PropertyModel) iter.next();
				propertyModelRemoved(prop);				
			}
			notifyPropertyChanged(oldValue, this);
			return result;
		}
		
		public PropertyModel set(int index, PropertyModel element){
			Object oldValue = super.clone();
			PropertyModel toRemove = get(index);
			propertyModelRemoved(toRemove);
			PropertyModel result = super.set(index, element);
			propertyModelAdded(result);
			notifyPropertyChanged(oldValue, this);
			return result;						
		}
				
	}

	protected void propertyModelAdded(PropertyModel prop){
		prop.addPropertyChangedListener(this);
	}
	
	protected void propertyModelRemoved(PropertyModel prop){
		prop.removePropertyChangedListener(this);
	}
	
	protected void clearPropertyChangedListeners(){
		for (PropertyModel prop : getValue()) {
			prop.removePropertyChangedListener(this);		
		}
	}
		
	public void propertyChanged(PropertyChangedEvent event) {
		notifyPropertyChanged(event.getOldValue(), event.getNewValue());
		
	}

	public void addNewItem(){
		try {
            throw new UnsupportedOperationException();
//			PropertyModel pm = (PropertyModel)getDefaultChildType().newInstance();
//			pm.setValueItems(getValueItems());
//			value.add(pm);
		} catch (Exception e) {}
	}
	
}
